%%--------------------------------------------------------------------
%% Copyright (c) 2020-2021 DGIOT Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------

-module(dgiot_atomgit_decoder).
-include_lib("dgiot_atomgit.hrl").
-include_lib("dgiot/include/logger.hrl").
-protocol([?atomgit]).
-define(CRLF, "\r\n").
%% API
-export([parse_frame/2, to_frame/1]).

parse_frame(Buff, Opts) ->
    parse_frame(Buff, [], Opts).

parse_frame(<<>>, Acc, _Opts) ->
    {ok, Acc};

%% HJ 212-2017
%% 6.3 通讯协议数据结构
%%所有的通讯包都是由 ASCII 码（汉字除外，采用 UTF-8 码，8 位，1 字节）字符组成。通讯协议数
%%据结构如图 4 所示。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 定 义         |    类型        |  长度         |          描 述                                                       |
%------------------------------------------------------------------------------------------------------------------------
%%      包头     |    字符        |    2          |     固定为##                                                        |
%-------------------------------------------------------------------------------------------------------------------------
%%    数据段长度 |    十进制整型   |    2          |     数据段的 ASCII 字符数，例如：长 255，则写为“0255”                 |
%-------------------------------------------------------------------------------------------------------------------------
%%    数据段     |    字符        |  0 ≤ n ≤ 1024 |  变长的数据，详见 6.3.2 章节的表 3《数据段结构组成表》                 |
%-------------------------------------------------------------------------------------------------------------------------
%%    CRC 校验   |   十六进制整数  | 4             |  数据段的校验结果，CRC 校验算法见附录 A。接收到一条命令，             |
%%               |                |               |    如果 CRC 错误，执行结束                                          |
%-------------------------------------------------------------------------------------------------------------------------
%%    包尾     |    字符          |  2             |  固定为<CR><LF>（回车、换行）                                      |
%-------------------------------------------------------------------------------------------------------------------------
parse_frame(<<"##", Length:4/binary, Tail/binary>>, Acc, State) ->
    Len = binary_to_integer(Length, 10),
    {Rest1, Acc1} =
        case Len > -1 andalso Len < 1025 of
            true ->
                case Tail of
                    <<UserZone:Len/binary, Crc:4/binary, ?CRLF, Rest/binary>> ->
                        CheckCrc = dgiot_atomgit_utils:crc16(UserZone),
                        case Crc of
                            CheckCrc ->
                                {Rest, Acc ++ [parse_userzone(UserZone, State)]};
                            _ ->
                                {<<>>, Acc}
                        end;
                    _ ->
                        {<<>>, Acc}
                end;

            _ ->
                {<<>>, Acc}
        end,
    parse_frame(Rest1, Acc1, State);

parse_frame(<<_:8, Data/binary>> = _Rest, Acc, Opts) ->
    parse_frame(Data, Acc, Opts).


%%6.3.2 数据段结构组成
%% 数据段结构组成见表 3，表 3 中“长度”包含字段名称、‘=’、字段内容三部分内容。
%%  表 3 数据段结构组成表
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%|   名称             | 类型     |    长度 |    描述                                                                     |
%|----------------------------------------------------------------------------------------------------------------------
%| 请求编码 QN        | 字符     | 20      | 精确到毫秒的时间戳:QN=YYYYMMDDhhmmsszzz，用来唯一标识一次命令交互             |
%------------------------------------------------------------------------------------------------------------------------
%| 系统编码 ST        | 字符     | 5       | ST=系统编码, 系统编码取值详见 6.6.1 章节的表 5《系统编码表》                  |
%|----------------------------------------------------------------------------------------------------------------------
%| 命令编码 CN        | 字符     |  7      | CN=命令编码, 命令编码取值详见 6.6.5 章节的表 9《命令编码表》                  |
%|----------------------------------------------------------------------------------------------------------------------
%|  访问密码          |   字符   |   9     | PW=访问密码                                                                 |
%|----------------------------------------------------------------------------------------------------------------------|
%|                    |          |       | MN=设备唯一标识，这个标识固化在设备中，用于唯一标识一个设备。MN 由 EPC-96        |
%|                    |          |       |  编码转化的字符串组成，即 MN 由 24 个 0~9，A~F 的字符组成                       |
%|                    |          |       |     _______________________________________________________________          |
%| 设备唯一标识 MN     |   字符   | 27    |     |              EPC-96 编码结构                                 |         |
%|                    |          |       |     |------------------------------------------------------------- |        |
%|                    |          |       |     |   名称   |  标头  | 厂商识别代码 | 对象分类代码 |  序列号      |         |
%|                    |          |       |      ---------------------------------------------------------------         |
%|                    |          |       |     | 长度(比特 | 8     |   28         |   24        |  36         |         |
%|----------------------------------------------------------------------------------------------------------------------|
%|                    |          |       |   Flag=标志位，这个标志位包含标准版本号、是否拆分包、数据是否应答。             |
%|                    |          |       |   ——————————————————————————————————————————                                 |
%|                    |          |       |   | V5 | V4 | V3 | V2 | V1 | V0 | D | A    |                                 |
%|                    |          |       |   -------------------------------------------                                |
%|  拆分包及应答标志   | 整数     | 8     |   V5~V0：标准版本号；Bit：000000 表示标准 HJ/T 212-2005，000001                  |
%|      Flag          | （0-255）|       |   表示本次标准修订版本号。                                                      |
%|                    |          |       |   A：命令是否应答；Bit：1-应答，0-不应答。                                      |
%|                    |          |       |   D：是否有数据包序号；Bit：1-数据包中包含包号和总包数两部分,                    |
%|                    |          |       |     0-数据包中不包含包号和总包数两部分。                                       |
%|                    |          |       |  示例：Flag=7 表示标准版本为本次修订版本号，数据段需要拆分并且命令需要应答       |
%|----------------------------------------------------------------------------------------------------------------------|
%| 总包数PNUM         |  字符     |  9     |  PNUM 指示本次通讯中总共包含的包数,注：不分包时可以没有本字段，与标志位有关      |
%|----------------------------------------------------------------------------------------------------------------------|
%|   包号 PNO        |    字符    |   8    | PNO 指示当前数据包的包号,注：不分包时可以没有本字段，与标志位有关
%|----------------------------------------------------------------------------------------------------------------------|
%|  指令参数 CP      |    字符     | 0≤n≤950| CP=&&数据区&&，数据区定义见 6.3.3 章节                                      |
%|----------------------------------------------------------------------------------------------------------------------|
%%parse_userzone(<<"QN=",QN:17/binary,";ST=", ST:2/binary, ";CN=", CN:4/binary, ";PW=", PWD:6/binary, ";MN=", MN:24/binary, ";Flag=", Flag:2/binary, PNUM:9/binary, PNO:8/binary, ";CP=", CP/binary>>, _State) ->
%%  ##0331QN=20240204193300000;ST=31;CN=2011;PW=123456;MN=60436377;Flag=4;CP=&&DataTime=20240204193300;a34004-Rtd=10,a34004-Flag=N;a34002-Rtd=12,a34002-Flag=N;a34001-Rtd=26,a34001-Flag=N;a01001-Rtd=1.8,a01001-Flag=N;a01002-Rtd=91.0,a01002-Flag=N;a01007-Rtd=3.9,a01007-Flag=N;a01008-Rtd=314.00,a01008-Flag=N;a01006-Rtd=102.36,a01006-Flag=N;&&7F80\r\n
parse_userzone(UserZone, _State) ->
    lists:foldl(fun(X, Acc) ->
        case X of
            <<"MN=", Devaddr/binary>> ->
                Acc#{<<"devaddr">> => Devaddr};
            <<"CP=&&", CP/binary>> ->
                Acc#{<<"CP">> => dgiot_atomgit_utils:get_cps(CP)};
            _ ->
                NewX =
                    case re:split(X, <<",">>) of
                        [First, _] ->
                            First;
                        _ ->
                            X
                    end,
                case re:split(NewX, <<"=">>) of
                    [K, V] ->
                        Acc#{K => V};
                    _ -> Acc
                end
        end
                end, #{}, re:split(UserZone, <<";">>)).

to_frame(#{<<"QN">> := QN, <<"ST">> := ST, <<"CN">> := CN, <<"PW">> := PW, <<"MN">> := MN, <<"Flag">> := Flag, <<"CP">> := CP, <<"PNUM">> := PNUM, <<"PNO">> := PNO}) ->
    Rdata = <<"QN=", QN/binary, ";ST=", ST/binary, ";CN=", CN/binary, ";PW=", PW/binary, ";MN=", MN/binary,
        ";Flag=", Flag/binary, ";PNUM=", PNUM/binary, ";PNO=", PNO/binary, ";CP=&&", CP/binary, "&&">>,
    Len = dgiot_atomgit_utils:get_len(Rdata),
    Crc = dgiot_atomgit_utils:crc16(Rdata),
    <<"##", Len/binary, Rdata/binary, Crc/binary, "\r\n">>;

to_frame(#{<<"QN">> := QN, <<"ST">> := ST, <<"CN">> := CN, <<"PW">> := PW, <<"MN">> := MN, <<"Flag">> := Flag, <<"CP">> := CP}) ->
    Rdata = <<"QN=", QN/binary, ";ST=", ST/binary, ";CN=", CN/binary, ";PW=", PW/binary, ";MN=", MN/binary, ";Flag=", Flag/binary, ";CP=&&", CP/binary, "&&">>,
    Len = dgiot_atomgit_utils:get_len(Rdata),
    Crc = dgiot_atomgit_utils:crc16(Rdata),
    <<"##", Len/binary, Rdata/binary, Crc/binary, "\r\n">>.

